package ArithmeticObjects;
import java.math.BigInteger;

import utils.ByteArrUtils;

/**
 * Representing a large integer number
 * @author Idan
 */
public class LargeNumber extends java.math.BigInteger {

	private static final long serialVersionUID = 1L;
	
	public static LargeNumber TWO = new LargeNumber(2);
	public static LargeNumber ONE = new LargeNumber(1);
	public static LargeNumber ZERO = new LargeNumber(0);

	/**
	 * creates a LargeNumber from a byte array representing it
	 * @param val byte array representing the LargeNumber's value
	 */
	public LargeNumber(byte[] val) {
		super(val);
	}
	
	/**
	 * creates a LargeNumber from an integer representing it
	 * @param num an integer representing the LargeNumber
	 */
	public LargeNumber(int num) {
		super(ByteArrUtils.intToByteArray(num));
	}
	
	/**
	 * Helper constructor to create a LargeNumber from a BigInteger (java.math object)
	 * @param b a BigInteger object representing the LargeNumber
	 */
	public LargeNumber(BigInteger b)
	{
		super(b.toByteArray());
	}
	
	/**
	 * adds the value in objectA to the current instance and returns it
	 * @param objectA the LargeNumber to add to the current instance
	 * @return the sum of the current instance and objectA 
	 */
	public LargeNumber add(LargeNumber objectA) {
		return new LargeNumber(super.add((BigInteger)objectA));
	}
	
	/**
	 * multiplies the value in objectA with the current instance and returns it
	 * @param objectA the LargeNumber to multiply with the current instance
	 * @return the product of the current instance and objectA 
	 */
	public LargeNumber multiply(LargeNumber objectA) {
		return new LargeNumber(super.multiply((BigInteger)objectA));
	}
	
	/**
	 * subtracts the value in objectA from the current instance and returns it
	 * @param objectA the LargeNumber to subtract from the current instance
	 * @return the difference between the current instance and objectA 
	 */
	public LargeNumber subtract(LargeNumber objectA) {
		return new LargeNumber(super.subtract((BigInteger)objectA));
	}
	
	/**
	 * divides the value in objectA by the current instance and returns it
	 * @param objectA the LargeNumber to divide by the current instance
	 * @return the division of the current instance and objectA 
	 */
	public LargeNumber divide(LargeNumber objectA) {
		return new LargeNumber(super.divide((BigInteger)objectA));
	}
	
	/**
	 * calculates the remainder of the current instance divided by objectA and returns it
	 * @param objectA the LargeNumber to divide by the current instance
	 * @return the remainder of the current instance divided by objectA 
	 */
	public LargeNumber remainder(LargeNumber objectA) {
		return new LargeNumber(super.remainder((BigInteger)objectA));
	}
	
	/**
	 * raises the current instance by the power of exponent
	 * @param exponent the LargeNumber to raise the current instance by
	 * @return the current instance raised by the power of exponent 
	 */
	public LargeNumber power(LargeNumber exponent) throws Exception{
		if (exponent.bitLength() > 32)
			throw new Exception("Cannot raise a large number to the power of a large number");
		return new LargeNumber(super.pow(exponent.intValue()));
	}
	
	/**
	 * calculates the current instance modulo the given order
	 * @param order the LargeNumber the current instance divides by 
	 * @return the current instance modulo the given order 
	 */
	public LargeNumber mod(LargeNumber order) {
		return new LargeNumber(super.mod((BigInteger)order));
	}
	
	/**
	 * raises the current instance by the power of exponent modulo the given order
	 * @param exponent the exponent used for the calculation
	 * @param order the order used for the calculation
	 * @return the current instance by the power of exponent modulo the given order
	 */
	public LargeNumber modPow(LargeNumber exponent, LargeNumber order) {
		return new LargeNumber(super.modPow((BigInteger)exponent, (BigInteger)order));
	}
	
	/**
	 * compares the current instance's value to objectA
	 * @param objectA the LargeNumber to compare to
	 * @return 0 if the values are equal, a negative number if the current instance is 
	 * smaller than objectA and a positive number if it is higher than objectA
	 */
	public int compareTo(LargeNumber objectA) {
		return super.compareTo((BigInteger)objectA);
	}
	
	/**
	 * returns the bit length of the current instance's representation as a BigInteger
	 */
	public int bitLength() {
		return super.bitLength();
	}
	
	/**
	 * returns the current instance's representation as a byte array
	 */
	public byte[] toByteArray() {
		return super.toByteArray();
	}
	
}